package cn.itcast;

import cn.itcast.dao.GoodsRepository;
import cn.itcast.pojo.Goods;
import com.alibaba.fastjson.JSON;
import org.apache.commons.beanutils.BeanUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringDataEsManager {
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;
    @Autowired
    private GoodsRepository goodsRepository;
    //继承ElasticsearchRepository之后就会有增删改查的方法(针对es库)

    @Test
    public void addIndexAndMapping(){
        //根据实体中的注解信息创建库,此时不会创建type
       //elasticsearchTemplate.createIndex(Goods.class);
       //注意创建库和创建映射要独立执行,不可以一块执行
       //根据实体中的注解创建映射
        elasticsearchTemplate.putMapping(Goods.class);
    }
    //新增或修改
    @Test
    public void addDocument(){
       // 新增或修改save or update可以通用
       /* Goods goods = new Goods(1L,"大米6X手机","手机","大米",1199.0,"http.jpg");
        goodsRepository.save(goods);*/
       //根据id查询
      /*  Optional<Goods> goods = goodsRepository.findById(1L);
        Goods good = goods.get();
        System.out.println(good);*/
//批量添加
        List<Goods> list = new ArrayList<>();
        list.add(new Goods(1L, "小米手机7", "手机", "小米", 3299.00,"http://image.leyou.com/13123.jpg"));
        list.add(new Goods(2L, "坚果手机R1", "手机", "锤子", 3699.00,"http://image.leyou.com/13123.jpg"));
        list.add(new Goods(3L, "华为META10", "手机", "华为", 4499.00,"http://image.leyou.com/13123.jpg"));
        list.add(new Goods(4L, "小米Mix2S", "手机", "小米", 4299.00,"http://image.leyou.com/13123.jpg"));
        list.add(new Goods(5L, "荣耀V10", "手机", "华为", 2799.00,"http://image.leyou.com/13123.jpg"));
       goodsRepository.saveAll(list);
    }
    //查询
    @Test
    public void queryDocument(){
        //查询所有
        //Iterable<Goods> iterable = goodsRepository.findAll();
        //排序查询
       // Iterable<Goods> iterable = goodsRepository.findAll(Sort.by(Sort.Direction.ASC, "price"));
        //分页查询&&同时可以进行排序
        Page<Goods> iterable = goodsRepository.findAll(PageRequest.of(0, 3,Sort.by(Sort.Direction.ASC, "price")));
        for (Goods goods : iterable) {
            System.out.println(goods);
        }

    }
    //自定义查询
    @Test
    public void queryDocument1(){
        //只需要在接口中定义方法即可,不需要写该方法的实现
        //只能针对简单的查询
        List<Goods> goodsList = goodsRepository.findByBrandAndCategoryAndPriceBetween("小米","手机",4000.0,5000.0);
        for (Goods goods : goodsList) {
            System.out.println(goods);
        }

    }

    //SDe结合原生Api查询,网站多使用此方法
    //类似于京东,用户可能针对多个字段筛选查询,但是我们并不知道用户要根据哪几个字段(组合)
    @Test
    public void queryDocument2(){
        //构建原生查询器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //按term查询
       // nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery("title", "小米"));
//分页排序查询
        nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
       nativeSearchQueryBuilder.withPageable(PageRequest.of(0,3,Sort.by(Sort.Direction.DESC,"price")));
        //以分页的方式查询
        AggregatedPage<Goods> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), Goods.class);
        List<Goods> content = aggregatedPage.getContent();
        for (Goods goods : content) {
            System.out.println(goods);
        }
    }

    //SDE结合原生Api查询,网站多使用此方法,聚合查询
    @Test
    public void queryDocument3(){
        //构建原生查询器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("brandAvg").field("brand"));
        //以分页的方式查询
        AggregatedPage<Goods> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), Goods.class);
        //获取聚合的条件及数量
        Aggregations aggregations = aggregatedPage.getAggregations();
        Terms terms = aggregations.get("brandAvg");
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            System.out.println(bucket.getKeyAsString()+bucket.getDocCount());
        }
        //获取文档内容
        List<Goods> content = aggregatedPage.getContent();
        for (Goods goods : content) {
            System.out.println(goods);
        }
    }
//SDE结合原生Api查询,构建高亮
    @Test
    public void queryDocument4() {
        //构建原生查询器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery("title","小米"));
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("brandAvg").field("brand"));
        //以分页的方式查询
        //构建高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        nativeSearchQueryBuilder.withHighlightBuilder(highlightBuilder);
        //一定要有这一步.指定高亮域名
        nativeSearchQueryBuilder.withHighlightFields(new HighlightBuilder.Field("title"));
        //需要自己定义类来处理高亮条件
        AggregatedPage<Goods> aggregatedPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(),
                Goods.class,
                new GoodsHighLightResultMapper());
        //获取聚合的条件及数量
        Aggregations aggregations = aggregatedPage.getAggregations();
        Terms terms = aggregations.get("brandAvg");
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            System.out.println(bucket.getKeyAsString() + bucket.getDocCount());
        }
        //获取文档内容
        List<Goods> content = aggregatedPage.getContent();
        for (Goods goods : content) {
            System.out.println(goods);
        }
    }


    class GoodsHighLightResultMapper implements SearchResultMapper {
        @Override
        public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
            List<T> content = new ArrayList<>();
            Aggregations aggregations = searchResponse.getAggregations();
            String scrollId = searchResponse.getScrollId();
            SearchHits searchHits = searchResponse.getHits();
            long total = searchHits.getTotalHits();
            float maxScore = searchHits.getMaxScore();
            for (SearchHit searchHit : searchHits) {
                String sourceAsString = searchHit.getSourceAsString();
                T t = JSON.parseObject(sourceAsString, aClass);

                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                HighlightField highlightField = highlightFields.get("title");
                String title = highlightField.getFragments()[0].toString();
                try {
                    BeanUtils.setProperty(t, "title", title);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                content.add(t);
            }


            return new AggregatedPageImpl<T>(content, pageable, total, aggregations, scrollId, maxScore);
        }
    }
}
